import pytz
import math
from datetime import datetime
from itertools import groupby

from odoo import fields, models, api, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.tools import float_is_zero, float_compare


class ResearchAndDevelopmentOrder(models.Model):
    _name = 's.research.and.development.order'
    _description = 'Research and Development Order'
    _order = 'id desc'

    name = fields.Char('Name', copy=False, readonly=True, index=True, default='New', required=True)
    date = fields.Datetime('Order date', default=fields.Datetime.now)
    company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company, required=True)
    user_id = fields.Many2one('res.users', string='User', default=lambda self: self.env.user.id, required=True)
    confirm_date = fields.Datetime('Confirm date')
    planned_receiving_date = fields.Datetime('Planned delivery date')
    state = fields.Selection([
        ('draft', 'draft'),
        ('confirm', 'confirm'),
        ('part_feed', 'Part Feed'),
        ('done_feed', 'Done Feed'),
        ('cancel', 'cancel'),
    ], string='state', default='draft', copy=False)
    note = fields.Text(string='Note')
    mrp_production_count = fields.Integer(string='production count', compute='_compute_mrp_production_count')
    line_ids = fields.One2many('s.research.and.development.order.line', 'order_id')

    workflow_template_id = fields.Many2one(
        'sb.workflow.template', string="Approval Workflow Template", required=True,
        domain="[('sb_workflow_template_item_ids', '!=', False), ('workflow_type', 'in', ['other', 'sale'])]")
    workflow_id = fields.Many2one('sb.workflow', string="Current Approval Workflow", copy=False)
    workflow_state = fields.Selection(related="workflow_id.state", store=True, readonly=True, string="Approval Status")
    workflow_item_ids = fields.One2many(related="workflow_id.sb_workflow_item_ids", string="Approval Items")
    workflow_ids = fields.One2many('sb.workflow', 'development_id', string="Approval Workflows")
    workflow_ids_sum = fields.Integer(compute="_compute_workflow_ids_sum", store=True)
    product_ids = fields.One2many('product.product', compute='_compute_product_ids', string='Development Product')

    order_type = fields.Selection([
        ('N', 'NEW'),
        ('R', 'REPEAT'),
        ('C', 'CHANGE'),
    ], string="Order Type", copy=False, required=True)

    @api.depends('line_ids.product_id')
    def _compute_product_ids(self):
        for rec in self:
            rec.product_ids = rec.line_ids.mapped('product_id')

    @api.constrains('workflow_state')
    def constrains_workflow_state(self):
        for rec in self:
            if rec.workflow_state == 'approve' and rec.state == 'draft':
                rec.action_confirm()

    @api.depends('workflow_ids')
    def _compute_workflow_ids_sum(self):
        for rec in self:
            rec.workflow_ids_sum = len(rec.workflow_ids)

    def create_workflow(self):
        self.ensure_one()
        if not self.planned_receiving_date:
            raise UserError(_('The planned delivery date cannot be empty!'))
        if self.workflow_template_id:
            if len(self.workflow_ids) > 0:
                workflow_sum = len(self.workflow_ids) + 1
            else:
                workflow_sum = 1
            # 创建审批流
            new_wf_id = self.env['sb.workflow'].sudo().create({
                'name': self.name + _('-Approval Flow') + '-' + str(workflow_sum),
                'approval_time': fields.Datetime.now(),
                'workflow_type': 'development',
                'development_id': self.id,
                'product_id': self.line_ids[0].product_id.id if self.line_ids else False,
                'qty': self.line_ids[0].product_qty if self.line_ids else 0,
                'remark': self.note or ''
            })
            self.workflow_id = new_wf_id.id
            # 根据模板生成审批项
            item_audit_levels = []
            for item in self.workflow_template_id.sb_workflow_template_item_ids:
                new_item = self.env['sb.workflow.item'].sudo().create({
                    'name': item.name,
                    'user_group_ids': item.user_group_ids.ids,
                    'audit_level': item.audit_level,
                    'sb_workflow_id': new_wf_id.id,
                    'state': 'wait_forward',
                    'is_plan': item.is_plan
                })
                # 收集生成的审核项的所有层级
                item_audit_levels.append(new_item.audit_level)
            # 去重排序
            item_audit_levels = list(set(item_audit_levels))
            item_audit_levels.sort()
            # 开放最低层级的神匹配
            for n_item in new_wf_id.sb_workflow_item_ids:
                if n_item.audit_level == item_audit_levels[0]:
                    n_item.state = 'approval'
        else:
            raise ValidationError(_('Please select an approval template.'))

    def _compute_mrp_production_count(self):
        for rec in self:
            rec.mrp_production_count = len(rec.line_ids.mapped('mrp_production_ids'))

    def action_view_mrp_production(self):
        self.ensure_one()
        mrp_production_ids = self.line_ids.mapped('mrp_production_ids').ids
        action = {
            'res_model': 'mrp.production',
            'type': 'ir.actions.act_window',
        }
        if len(mrp_production_ids) == 1:
            action.update({
                'view_mode': 'form',
                'res_id': mrp_production_ids[0],
            })
        else:
            action.update({
                'name': _("%s Child MO's", self.name),
                'domain': [('id', 'in', mrp_production_ids)],
                'view_mode': 'tree,form',
            })
        return action

    def action_view_workflow(self):
        self.ensure_one()
        return {
            'name': 'Approval Flow',
            'type': 'ir.actions.act_window',
            'res_model': 'sb.workflow',
            'view_mode': 'tree,form',
            'target': 'current',
            'domain': [('development_id', '=', self.id)],
        }

    def action_confirm(self):
        self.ensure_one()
        self.write({'state': 'confirm', 'confirm_date': datetime.now()})

    @api.model_create_multi
    def create(self, vals_list):
        for vals in vals_list:
            if vals.get('name', "New") == "New":
                vals['name'] = self.env['ir.sequence'].next_by_code('s.research.and.development.order') or "New"

        return super().create(vals_list)


class ResearchAndDevelopmentOrderLine(models.Model):
    _name = 's.research.and.development.order.line'
    _description = 'Research and Development Order Line'

    order_id = fields.Many2one('s.research.and.development.order')
    product_id = fields.Many2one('product.product', string='Product', domain="[('detailed_type', '=', 'product'),('categ_id.code', '=', 'CHP')]", required=True)
    product_qty = fields.Float('Quantity', required=True)
    uom_id = fields.Many2one('uom.uom', string='Uom', required=True)
    planned_qty = fields.Float(string='Planned quantity', compute='compute_remaining_planned_qty', store=True, readonly=False, copy=False)
    remaining_planned_qty = fields.Float(string='Remaining planned quantity', compute='compute_remaining_planned_qty', store=True, readonly=False, copy=False)
    mrp_production_ids = fields.Many2many('mrp.production', copy=False)
    state = fields.Selection(string='Status', related='order_id.state', store=True)

    @api.constrains('product_qty')
    def constrains_product_qty(self):
        for rec in self:
            if float_is_zero(rec.product_qty, precision_rounding=rec.uom_id.rounding):
                raise ValidationError(_('The quantity of R&D order details must be greater than 0, current qty is :{qty}').format(qty=rec.product_qty))

    @api.onchange('product_id')
    def onchange_product_id(self):
        if self.product_id:
            self.uom_id = self.product_id.uom_id
        else:
            self.uom_id = None

    @api.depends('mrp_production_ids.state', 'product_qty')
    def compute_remaining_planned_qty(self):
        for rec in self:
            # 过滤掉状态为 'cancel' 的生产订单
            valid_productions = rec.mrp_production_ids.filtered(lambda x: x.state != 'cancel')

            # 计算总的计划数量
            total_planned_qty = sum(valid_productions.mapped('product_uom_qty'))
            temporary_qty = 0

            if not total_planned_qty:
                rec.planned_qty = 0
                rec.remaining_planned_qty = rec.product_qty
                continue

            all_development_line_ids = rec.mrp_production_ids.mapped('s_development_line_ids') \
                .filtered(lambda x: x.product_qty > 0) \
                .sorted(key=lambda x: x.id)

            total_development_qty = sum(all_development_line_ids.mapped('product_qty'))

            for line in all_development_line_ids:
                if line == all_development_line_ids[-1]:
                    line.planned_qty = total_planned_qty - temporary_qty
                else:
                    line.planned_qty = math.ceil(total_planned_qty * (total_development_qty / line.product_qty))
                    temporary_qty += line.planned_qty
                line.remaining_planned_qty = max(line.product_qty - line.planned_qty, 0)

    def action_feeding(self):

        for rec in self:
            if rec.product_id.detailed_type != 'product':
                raise UserError('The product is not allowed to be produced')

        if len(self.mapped('product_id')) > 1:
            raise UserError(_('Different products are not allowed to be combined for feeding!'))
        mo_type = self._context.get('mo_type', '')
        action = {
            'name': _('Planned distribution of materials'),
            'type': 'ir.actions.act_window',
            'view_mode': 'form',
            'res_model': 'plan.material.requirement.wizard',
            'target': 'new',
        }
        if mo_type == 'M':
            action.update({
                'res_model': 's.supp.plan.material.requirement.wizard',
                'context': self._context
            })
        return action
